package com.m3958.firstgwt.server.servlet;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;
import nl.captcha.Captcha;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.persist.Transactional;
import com.m3958.firstgwt.client.types.ServerErrorCode;
import com.m3958.firstgwt.server.constants.PeriodContants;
import com.m3958.firstgwt.server.dao.UserDao;
import com.m3958.firstgwt.server.model.AsyncTask;
import com.m3958.firstgwt.server.model.PersistedLogin;
import com.m3958.firstgwt.server.model.User;
import com.m3958.firstgwt.server.service.AppUtilService;
import com.m3958.firstgwt.server.service.IframeResultStoreService;
import com.m3958.firstgwt.server.service.MySHAService;
import com.m3958.firstgwt.server.service.ReqParaService;
import com.m3958.firstgwt.server.service.RequestScopeObjectService;
import com.m3958.firstgwt.server.session.ErrorMessages;
import com.m3958.firstgwt.server.session.SessionUser;
import com.m3958.firstgwt.server.types.AppConstants;
import com.m3958.firstgwt.server.types.SmartError;
import com.m3958.firstgwt.shared.type.CookieNames;

@Singleton
public class NotRpcLogin extends HttpServlet{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	

	@Inject
	private AppUtilService apputils;
	
	@Inject
	private Injector injector;
	
	@Inject
	private IframeResultStoreService irss;
	
	@Inject
	private com.google.inject.Provider<EntityManager> emp;
	
	
	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse res) 
	                               throws ServletException, IOException {
		doPost(req, res);
	}
	
	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse res) 
	                               throws ServletException, IOException {
		
		ReqParaService reqps = injector.getInstance(ReqParaService.class);
		
		String userName = req.getParameter("username");
		String password = req.getParameter("password");
		String want = req.getParameter("want");
		String answer = req.getParameter("answer");
		String lo = req.getParameter("logout");
		
		String dowhat = req.getParameter("dowhat");
		res.setHeader("Accept-Language", req.getHeader("Accept-Language"));
		SessionUser su = injector.getInstance(SessionUser.class);
		UserDao udao = injector.getInstance(UserDao.class);
		if(dowhat != null && !dowhat.isEmpty()){
			if("fake2user".equals(dowhat) && userName != null){
				if(su.isSuperman()){
					User fu = udao.findByLoginNameOrEmailOrMobile(userName);
					if(fu != null){
						if(fu.getBindTo()>0){
							fu = udao.find(fu.getBindTo());
						}
					}
					
					if(fu != null){
						su.setContent(fu);
						apputils.writeHtmlResponse(res, "true");
						return;
					}
				}
			}
			apputils.writeHtmlResponse(res, "false");
			return;
		}
		
		ErrorMessages errors = injector.getInstance(ErrorMessages.class);
		
		if(!reqps.getStringValue("_loginstatusdetect", "").isEmpty()){
			User u = null;
			boolean isJsonp = reqps.isJsonp();
			JSONObject result = null;
			if(su.getLoginStatus()){
				u = emp.get().find(User.class, su.getUserId());
//				JSONObject userJson = apputils.getListResponse(u,reqps.getJsonv()); 
//				apputils.addM3958ExtraField(userJson,reqps.getLoginExtraTask());
//				result = userJson;
			}else{
				Cookie[] cookies = req.getCookies();
				PersistedLogin pl = null;
				JSONObject cookievalue = null;
				if(cookies != null && cookies.length>0){
					for(Cookie c : cookies){
						if(CookieNames.PERSISTED_LOGIN.equals(c.getName())){
							String s = URLDecoder.decode(c.getValue(),"utf-8");
							cookievalue = JSONObject.fromObject(s);//得到的cookie没有path和maxage信息。
							pl = findPersistedLogin(cookievalue);
							break;
						}
					}
				}
				if(pl != null){//从cookie值里面找到了保存的persistedLogin
					
					u = udao.findByLoginNameOrEmailOrMobile(pl.getLoginName());
					if(u != null){
						su.setContent(u,true);
//						JSONObject userJson = apputils.getListResponse(u,reqps.getJsonv()); 
//						apputils.addM3958ExtraField(userJson,reqps.getLoginExtraTask());
//						result = userJson;
					}else{
//						result = apputils.getEmptyListResponse();
						;
					}
					removePersistedLoginCookie(pl);
					PersistedLogin npl = new PersistedLogin(u.getEmail());
					savePersistLogin(npl);
					updateCookie(res,npl,cookievalue);
				}else{//没有找到persistedLogin，有可能是没有对应的cookie（用户没有选择保持登录）
					if(cookievalue != null){//提供了cookie但是没有找到对应的记录，可能发生了攻击
						removeUsersPersistedLogin(cookievalue);
						errors.addError(new SmartError("您的帐号状态异常，请及时更换密码！", ServerErrorCode.ACCOUNT_ATTACKED));
						result = apputils.getJsonErrorResponseJsonObject(res);
						removePersistedLoginCookie(res, cookievalue);
//						String s = persistedCookie.getValue();
						//TODO 删除所有该用户相关的记录
					}else{
//						result = apputils.getEmptyListResponse();
						;
					}
				}
			}
			
			if(u!=null){
				if(u.getAvatar() == null || u.getAvatar().isEmpty()){
					u.setAvatar("/images/icons/32/blankavatar.png");
				}
				JSONObject userJson = apputils.getListResponse(u,reqps.getJsonv()); 
				apputils.addM3958ExtraField(userJson,reqps.getLoginExtraTask());
				result = userJson;
			}else{
				result = apputils.getEmptyListResponse();
			}
//			Cache-Control: no-cache, must-revalidate
//			res.setHeader("Cache-Control","max-age=" + PeriodContants.YEAR_IN_SECONDS * 10);
			res.setHeader("Cache-Control","no-cache, must-revalidate");
			result.element("jsessionid", req.getSession().getId());
			apputils.addM3958ExtraField(result,"uuid");
			if(isJsonp){
				apputils.writeJsonpResponse(res, result.toString(),reqps.getCallbackName());
			}else{
				apputils.writeJsonResponse(res, result.toString());
			}
			return;
		}
		
		
		if(reqps.hasBianmaChecker()){
			userName = reqps.transBianma(userName);
		}
		
		if(lo != null && lo.length() > 0){
			su.clearContent();
			removeCookie(res, CookieNames.PERSISTED_LOGIN,"/notrpclogin");
			if(reqps.isJsonp()){
				apputils.writeJsonpResponse(res, "{\"logout\":true}", req.getParameter("callback"));
			}else{
				String next = req.getParameter("next");
				if(next == null){
					next = "sitebuilder";
				}
				
				
			if(req.getParameter("hostname") == null || req.getParameter("hostname").isEmpty()){
				res.sendRedirect("/login?next=" + next + "&debug=true");
			}else{
				RequestScopeObjectService rso = injector.getInstance(RequestScopeObjectService.class);
				res.sendRedirect("http://" + rso.getRequestHostName() + "/login?next=" + next);
			}

				
//				if(req.getParameter("hostname") == null || req.getParameter("hostname").isEmpty()){
//					res.sendRedirect("/login.html#next=" + next + "&debug=true");
//				}else{
//					RequestScopeObjectService rso = injector.getInstance(RequestScopeObjectService.class);
//					res.sendRedirect("http://" + rso.getRequestHostName() + "/login.html#next=" + next);
//				}
			}
			return;
		}
		
		
		Captcha captcha = (Captcha) req.getSession().getAttribute(Captcha.NAME);
		if(captcha == null || !captcha.isCorrect(answer)){
			errors.addError(new SmartError("验证码错误！", ServerErrorCode.CAPTCHA_ERROR));
		}
		User user = null;
		if(errors.isEmpty()){
			req.getSession().removeAttribute(Captcha.NAME);
			user = udao.findByLoginNameOrEmailOrMobile(userName);
			boolean logined = false;
			if(user != null){
				MySHAService shas = injector.getInstance(MySHAService.class);
				if(shas.isEqual(user.getPassword(), password)){
					if(reqps.getStringValue("_bindexist", "").isEmpty()){
						su.setContent(user);
					}else{
						if(su.isLogined()){
							User fcu = udao.find(su.getUserId());
							if(fcu.getFcUser() && fcu.getBindTo() < 1){
								if(user.getId() != fcu.getId()){//开始绑定
									fcu.setBindTo(user.getId());
									udao.update(fcu);
									//同一个transaction里面不可能完成的任务。
//									user.setFcId(fcu.getFcId());
//									user.setFcUser(true);
//									fcu.setFcId(UUID.randomUUID().toString());
//									dao.remove(fcu);
//									fcu = null;
//									dao.smartUpdateBaseModel(user);
//									AsyncTask at = new AsyncTask();
//									at.setCatagory(AsyncTask.TaskCat.BIND_USER.toString());
//									at.setStatus(AsyncTask.TaskStatus.UNTOUCHED.toString());
//									at.setName("绑定帐号");
//									at.setCreatedAt(new Date());
//									at.setTaskDescript("=,target=" + user.getId() + "=src=" + fcu.getId());
//									persisteAsyncTask(at);
									su.setContent(user);
								}
							}
						}
					}
					logined = true;
				}
			}
			if(!logined){
				errors.addError(new SmartError("用户名或密码错误！", ServerErrorCode.LOGIN_FAILURE));
			}			
		}
		
		String tid = reqps.getTid();
		JSONObject results = null;
		
		JSONObject m3958jo = new JSONObject();
		m3958jo.element("tid", reqps.getTid());
		
		if("redirect".equals(want)){
			if(!errors.isEmpty()){
				res.sendRedirect("/login.html#next=" + req.getParameter("next") + "&username=" + req.getParameter("username"));
			}else{
				res.sendRedirect("/in?locale=" + req.getLocale().getLanguage());
			}
		}else{
			if(!errors.isEmpty()){
				results = apputils.getJsonErrorResponseJsonObject(res);
			}else{
				results = apputils.getListResponse(user,reqps.getJsonv());
				if(!reqps.getStringValue(CookieNames.PERSISTED_LOGIN,"").isEmpty()){
					setPersitedLoginCookie(user,req,res,reqps);
				}else{
					;
				}
			}
			results.getJSONObject("response").element(AppConstants.M3958EXTRA, m3958jo);
			apputils.addM3958ExtraField(results, reqps.getLoginExtraTask());
			
			results.getJSONObject("response").element("jsessionid", req.getSession().getId());
			if("json".equals(want)){
				apputils.writeJsonResponse(res, results.toString());
			}else if(reqps.isIframe()){
				irss.addIframeResult(tid, results.toString());
				apputils.writeHtmlResponse(res, "");
			}else{
				apputils.writeHtmlResponse(res, results.toString());
			}		
		}
	}
	
	@Transactional
	protected void persisteAsyncTask(AsyncTask at){
		emp.get().persist(at);
	}
	
	private void updateCookie(HttpServletResponse res, PersistedLogin pl,
			JSONObject cookievalue) throws UnsupportedEncodingException {
		Long d = cookievalue.getLong("expiredate");
		int maxage = new Long(((d - new Date().getTime())/1000)).intValue();
		cookievalue.element("seriesNum", pl.getSeriesNum());//新的值。
		cookievalue.element("token", pl.getToken());
		
		Cookie c = new Cookie(CookieNames.PERSISTED_LOGIN,URLEncoder.encode(cookievalue.toString(),"utf-8"));
		c.setPath(cookievalue.getString("path"));
		c.setMaxAge(maxage);
		res.addCookie(c);
	}
	
	private void removePersistedLoginCookie(HttpServletResponse res, JSONObject cookievalue) {
		Cookie c = new Cookie(CookieNames.PERSISTED_LOGIN,null);
		c.setPath(cookievalue.getString("path"));
		c.setMaxAge(0);
		res.addCookie(c);
	}
	
	private void removeCookie(HttpServletResponse res,String cookieName,String path){
		Cookie c = new Cookie(cookieName,null);
		c.setPath(path);
		c.setMaxAge(0);
		res.addCookie(c);
	}

	/*
	 * <script type="text/javascript">
	 * parent.iframeCallBack(flag,resstr);
	 * </script>
	 * this code will not work,because of same domain policy.
	 * */
	@Transactional
	public void setPersitedLoginCookie(User user,HttpServletRequest req, HttpServletResponse res,ReqParaService reqps) throws UnsupportedEncodingException{
		//email,series,token
		PersistedLogin pl = new PersistedLogin(user.getEmail());
		emp.get().persist(pl);
		String maxage = reqps.getStringValue(CookieNames.PERSISTED_LOGIN,"");
		String value = createCookieValue(pl,maxage);
		Cookie c = new Cookie(CookieNames.PERSISTED_LOGIN, URLEncoder.encode(value,"utf-8"));
		c.setPath("/notrpclogin");
		c.setMaxAge(getPersistLoginAge(maxage));
		res.addCookie(c);
	}
	
	private String createCookieValue(PersistedLogin pl,String maxage){
		JSONObject jo = pl.toJson();
		jo.element("path", "/notrpclogin");
		long d = new Date().getTime() + getPersistLoginAge(maxage)*1000; 
		jo.element("expiredate", d);
		return jo.toString();
	}
	
	@Transactional
	public void removePersistedLoginCookie(PersistedLogin pl){
		emp.get().remove(pl);
	}
	
	
	@Transactional
	public void removeUsersPersistedLogin(JSONObject  cookieJson){
		String email = cookieJson.getString("loginName");
		Query q = emp.get().createQuery("DELETE FROM PersistedLogin p WHERE p.loginName = :loginName");
		q.setParameter ("loginName", email);
		q.executeUpdate ();
	}
	
	@Transactional
	public void savePersistLogin(PersistedLogin pl){
		emp.get().persist(pl);
	}


	private int getPersistLoginAge(String maxage) {
		if(maxage.isEmpty()){
			return parseint(maxage) * PeriodContants.YEAR_IN_SECONDS;
		}else{
			if(maxage.endsWith("d")){
				return parseint(maxage) * PeriodContants.DAY_IN_SECONDS;
			}else if(maxage.endsWith("w")){
				return parseint(maxage) * PeriodContants.WEEK_IN_SECONDS;
			}else if(maxage.endsWith("m")){
				return parseint(maxage) * PeriodContants.MONTH_IN_SECONDS;
			}else if(maxage.endsWith("y")){
				return parseint(maxage) * PeriodContants.YEAR_IN_SECONDS;
			}else{
				return -1;
			}
		}
	}
	
	private int parseint(String maxage){
		return Integer.parseInt(maxage.substring(0, maxage.length() - 1));
	}
	
	public PersistedLogin findPersistedLogin(JSONObject jo){
		try {
			String qs = "SELECT p FROM PersistedLogin AS p WHERE p.loginName = :loginName AND p.seriesNum = :seriesNum AND p.token = :token";
			Query q = emp.get().createQuery(qs);
			q.setParameter("loginName", jo.getString("loginName"));
			q.setParameter("seriesNum", jo.getString("seriesNum"));
			q.setParameter("token", jo.getString("token"));
			return (PersistedLogin) q.getSingleResult();
		} catch (Exception e) {
			return null;
		}
	}

	@SuppressWarnings("unused")
	private String getJavaScript(String tid,String flag,String resStr){
		StringBuffer sb = new StringBuffer();
		sb.append("<script type=\"text/javascript\">").
		append("parent.iframeCallBack(").append("\"" + tid + "\"").append(",").append("\"" +flag + "\"").append(",").append(resStr).append(");").
		append("</script>");
		return sb.toString();
	}
}
